//
// Created by tang on 2022/1/10.
//

#include "diary_main_form.hpp"

#include <utility>
#include <queue>
#include "app/app_base.hpp"
#include "gui/components/script_view.hpp"
#include "imgui/imgui.h"
#include "imgui/implot.h"
#include "imgui/IconsFontAwesome4.h"
#include "ui_component.hpp"
#include "components/schedule_view.hpp"
#include "components/statistics_view.hpp"
#include "gui/components/app_host_area.hpp"
#include "gui/components/redmine_tools_view.hpp"
#include "gui/components/management_view.hpp"
#include "gui/components/template_management_view.hpp"
#include "data/app_config.hpp"
#include "dialogs/mainline_task_dialog.hpp"
#include "event/events.hpp"
#include "gui/dialogs/app_config_view.hpp"
#include "gui/components/diary_editor_view.hpp"
#include "gui/components/credits_view.hpp"
#include "gui/components/surveyor_view.hpp"
#include "gui/controls/custom_controls.hpp"
#include "gui/controls/cfg_view.hpp"
#include "gui/utils/window_helper.hpp"
#include "imgui/imgui_internal.h"
#include "fmt/format.h"
#include "fmt/chrono.h"
#include "utils/app_utils.hpp"
#include "utils/shell_utils.hpp"
#include "utils/misc_utils.hpp"

struct DiaryAppData
{
    std::unique_ptr<AppHostArea> root_area;
    Config::AppConfig& cfg = Config::AppConfig::Instance();
    Listener listener{ App->Bus() };
    double message_disappear_time{ 0. };
    bool last_frame_has_status_message{ false };
    std::queue<AppEvent::StatusMessage> messages;
};

DiaryAppMainForm::~DiaryAppMainForm()
{
    data->listener.unlistenAll();
    delete data;
}

void DiaryAppMainForm::OnDraw()
{
    using namespace ImGui;

    static auto sql = App->Sql();
    static bool inited = false;

    if (sql->IsConfigured()) {
        if (!inited) {
            inited = true;
            Init();
            App->SetupSqlDriver();
        }

        MainFromBase::OnDraw();

        auto mvp = GetMainViewport();

        // 状态栏
        if (BeginViewportSideBar("##StatusBar", mvp, ImGuiDir_Down, GetFrameHeight() * 1.2f,
            ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration |
            ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollWithMouse))
        {
            const float max_width = GetContentRegionAvail().x;

            SetCursorPosY(GetStyle().ItemSpacing.y);
            TextUnformatted(ICON_FA_STICKY_NOTE);
            if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left))
            {
                ShowLogFileContent();
            }
            const AppEvent::StatusMessage* msg = nullptr;
            if (data->messages.empty())
            {
                // show nothing
            }
            else if (data->last_frame_has_status_message)
            {
                // check if message is already timeout
                if (data->message_disappear_time != 0.0 && GetTime() >= data->message_disappear_time)
                {
                    // show next message or show nothing
                    if (data->messages.size() > 1)
                    {
                        data->messages.pop();
                        msg = &data->messages.front();
                        if (msg->timeout == 0.0)
                            data->message_disappear_time = 0.0;
                        else
                            data->message_disappear_time = GetTime() + data->messages.front().timeout / 1000.0;
                        data->last_frame_has_status_message = true;
                    }
                    else
                    {
                        data->messages.pop();
                        data->last_frame_has_status_message = false;
                    }
                }
                else if (data->message_disappear_time == 0.0)
                {
                    if (data->messages.size() > 1)
                    {
                        data->messages.pop();
                        msg = &data->messages.front();
                        if (msg->timeout == 0.0)
                            data->message_disappear_time = 0.0;
                        else
                            data->message_disappear_time = GetTime() + data->messages.front().timeout / 1000.0;
                    }
                    else
                        msg = &data->messages.front();
                }
                else
                {
                    msg = &data->messages.front();
                }
            }
            else
            {
                msg = &data->messages.front();
                if (msg->timeout == 0.0)
                    data->message_disappear_time = 0.0;
                else
                    data->message_disappear_time = GetTime() + data->messages.front().timeout / 1000.0;
                data->last_frame_has_status_message = true;
            }

            if (msg)
            {
                SameLine();
                TextColored(ImColor(0.0f, 0.85f, 0.15f), "%s", msg->msg.c_str());

                if (msg->op_cnt > 0)
                {
                    for (int i = 0; i < msg->op_cnt; ++i)
                    {
                        SameLine();
                        if (SmallButton(msg->operations[i].name.c_str()))
                        {
                            msg->operations[i].cb();
                            data->message_disappear_time = GetTime();
                        }
                    }
                }
            }

            // date and time
            char buffer[128]{ 0 };
            fmt::format_to_n(buffer, sizeof(buffer), ICON_FA_USER " {}  " ICON_FA_DESKTOP " {}  " ICON_FA_CALENDAR " {:%Y年%m月%d日 %H时%M分}",
                GetUsername(),
                GetHostname(),
                fmt::localtime(std::chrono::system_clock::now()));
            SameLine(max_width - CalcTextSize(buffer).x);
            TextColored(ImColor(0.8f, 0.9f, 0.0f), "%s", buffer);
        }
        End();

        // Create Main Area
        SetNextWindowPos(mvp->WorkPos);
        SetNextWindowSize(mvp->WorkSize);
        SetNextWindowViewport(mvp->ID);
        if (data->root_area)
            data->root_area->RenderComponent();
    }
    else
    {
        auto const mvp = GetMainViewport();
        SetNextWindowPos(mvp->WorkPos);
        SetNextWindowSize(mvp->WorkSize);
        PushStyleColor(ImGuiCol_WindowBg, IM_COL32(250,10,10,255));
        if (Begin("##NOTE", nullptr, ImGuiWindowFlags_NoTitleBar))
        {
            PushStyleColor(ImGuiCol_Text, IM_COL32(250,250,250,255));
            PushFont(nullptr, 64);
            CenteredText("点击配置数据库属性");
            PopFont();
            PopStyleColor(1);
            if (IsItemClicked(ImGuiMouseButton_Left)) {
                OpenPopup("数据库配置");
            }
            PlaceNextWindowCenterScreen(mvp->WorkSize * 0.75f);
            if (BeginPopupModal("数据库配置"))
            {
                RenderConfigView(sql->GetConfig());
                EndPopup();
            }
        }
        End();

        PopStyleColor(1);
    }
}

DiaryAppMainForm::DiaryAppMainForm(std::string title, int width, int height) : MainFromBase(std::move(title), width,
    height),
    data(new DiaryAppData{})
{
    data->listener.listen<AppEvent::StatusMessage>([this](auto const& e)
        {
            data->messages.emplace(e);
        });
}

void DiaryAppMainForm::SetupStyles()
{
    MainFromBase::SetupStyles();

    auto& style = ImGui::GetStyle();

    // Colors
    ImVec4* colors = ImGui::GetStyle().Colors;
    colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
    colors[ImGuiCol_TextDisabled] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f);
    colors[ImGuiCol_WindowBg] = ImVec4(0.15f, 0.22f, 0.30f, 1.00f);
    colors[ImGuiCol_ChildBg] = ImVec4(0.32f, 0.33f, 0.34f, 1.00f);
    colors[ImGuiCol_PopupBg] = ImVec4(0.16f, 0.24f, 0.23f, 1.00f);
    colors[ImGuiCol_Border] = ImVec4(0.22f, 0.41f, 0.72f, 0.58f);
    colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_FrameBg] = ImVec4(0.10f, 0.17f, 0.24f, 1.00f);
    colors[ImGuiCol_FrameBgHovered] = ImVec4(0.14f, 0.17f, 0.11f, 1.00f);
    colors[ImGuiCol_FrameBgActive] = ImVec4(0.18f, 0.18f, 0.04f, 1.00f);
    colors[ImGuiCol_TitleBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
    colors[ImGuiCol_TitleBgActive] = ImVec4(0.05f, 0.15f, 0.29f, 1.00f);
    colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
    colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
    colors[ImGuiCol_ScrollbarBg] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f);
    colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    colors[ImGuiCol_CheckMark] = ImVec4(0.93f, 0.93f, 0.93f, 1.00f);
    colors[ImGuiCol_SliderGrab] = ImVec4(0.93f, 0.93f, 0.93f, 1.00f);
    colors[ImGuiCol_SliderGrabActive] = ImVec4(0.42f, 0.57f, 0.09f, 1.00f);
    colors[ImGuiCol_Button] = ImVec4(0.42f, 0.57f, 0.09f, 1.00f);
    colors[ImGuiCol_ButtonHovered] = ImVec4(0.48f, 0.66f, 0.10f, 1.00f);
    colors[ImGuiCol_ButtonActive] = ImVec4(0.15f, 0.20f, 0.03f, 1.00f);
    colors[ImGuiCol_Header] = ImVec4(0.18f, 0.20f, 0.22f, 1.00f);
    colors[ImGuiCol_HeaderHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);
    colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
    colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);
    colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);
    colors[ImGuiCol_ResizeGrip] = ImVec4(0.66f, 0.81f, 0.98f, 0.20f);
    colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.76f, 0.87f, 1.00f, 0.67f);
    colors[ImGuiCol_ResizeGripActive] = ImVec4(0.83f, 0.91f, 1.00f, 0.95f);
    colors[ImGuiCol_Tab] = ImVec4(0.37f, 0.42f, 0.42f, 0.86f);
    colors[ImGuiCol_TabHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
    colors[ImGuiCol_TabSelected] = ImVec4(0.20f, 0.41f, 0.68f, 1.00f);
    colors[ImGuiCol_TabDimmed] = ImVec4(0.07f, 0.10f, 0.15f, 0.97f);
    colors[ImGuiCol_TabDimmedSelected] = ImVec4(0.14f, 0.26f, 0.42f, 1.00f);
    colors[ImGuiCol_DockingPreview] = ImVec4(0.26f, 0.59f, 0.98f, 0.70f);
    colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
    colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
    colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
    colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
    colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
    colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
    colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
    colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
    colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
    colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
    colors[ImGuiCol_DragDropTarget] = ImVec4(0.78f, 1.00f, 0.59f, 0.90f);
    colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
    colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
    colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);

    // Size
    style.FrameRounding = 4.f;
    style.PopupRounding = 4.f;
    style.ChildRounding = 4.f;
    style.WindowRounding = 4.f;
    style.GrabRounding = 8.f;
    style.ScrollbarRounding = 8.f;
    style.WindowPadding = { 8.f, 8.f };
    //style.FramePadding = { 6.f, 4.f };
    style.ItemSpacing = { 6.f, 4.f };
}

void DiaryAppMainForm::ShowDialog(const std::string& dlg_name)
{
    data->root_area->ShowPopup(dlg_name);
}

void DiaryAppMainForm::ShowDialog(unsigned int dlg_id)
{
    ImGui::OpenPopup(dlg_id);
}

void DiaryAppMainForm::Init()
{
    MainFromBase::Init();
    
    const auto& tab = (data->root_area = std::make_unique<AppHostArea>());
    tab->RegisterComponent(std::make_shared<DiaryEditorView>());
    tab->RegisterComponent(std::make_shared<RedMineToolsView>());
    tab->RegisterComponent(std::make_shared<AppConfigView>());
    tab->RegisterComponent(std::make_shared<ManagementView>());
    tab->RegisterComponent(std::make_shared<StatisticsView>());
    tab->RegisterComponent(std::make_shared<CreditsView>());
    tab->RegisterComponent(std::make_shared<TemplateManagementView>());
    tab->RegisterComponent(std::make_shared<ScheduleView>());
    tab->RegisterComponent(std::make_shared<MainlineTaskDialog>());
    tab->RegisterComponent(std::make_shared<ScriptView>());
    tab->RegisterComponent(std::make_shared<SurveyorView>());

    tab->OnInit();
}

void DiaryAppMainForm::Cleanup()
{
    if (data->root_area)
        data->root_area->OnCleanup();
    MainFromBase::Cleanup();
}
